home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-04-09 | 16.9 KB | 619 lines | [TEXT/KEEN] |
- #$Multi_Undo : for use with EnterAct's "••Recent Activities••"
- # listing only, regenerates an earlier version of a file in
- # $tempStdOut. Slightly complicated to get going, and subject
- # to some limitations, so please read instructions below before
- # proceeding.
-
- # Let's say you have a file called "A", with full path name
- # "Disk:folder1:...:folderN:A" that you've been
- # editing, and you wish to undo all the (saved) changes
- # you've made to it, back to a specific time or alteration.
- # Let's call the point back to which you wish to undo the
- # "undo point" for file A.
- # This program will do it, provided the circumstances are
- # as follows:
- # 1 file A currently does not need saving
- # 2 All changes to file A, from to the undo point up to
- # the present, have been made with EnterAct
- # 3 From the undo point up to the present, no other files
- # named "A" have been edited with EnterAct (ie a different
- # disk or folder, but the same basic file name A)
- # 4 The activity corresponding to the undo point is present
- # in EnterAct's "••Recent Activities••" window (it goes
- # back "only" 1024 activities).
- # 5 The entire text of each edit on file A is present in
- # "••Recent Activities••": you normally won't want to
- # check for this yourself, and this program will do it
- # thoroughly for you. In general, only inserts or deletes
- # below 32K characters are fully recorded, and older large
- # edits near the beginning of "••Recent Activities••" may
- # not be fully recorded if it was necessary to make room
- # for newer large edits. Rule of thumb: barring a sequence
- # of large edits, all of your edits will still be fully
- # recorded. Don't worry about it.
-
- # If your circumstances meet the above limitations, you can
- # undo changes to a particular file A back to a specific point
- # as follows:
- # 1 Open an EnterAct project
- # 2 If necessary, add file A to it
- # 3 Select "Show Activities..."; click on the "originally
- # recorded positions" button; and for the number of activities
- # to show, enter "1024"; and click the OK button to generate
- # the "••Recent Activities••" window
- # 4 Locate the oldest activity you wish undone, and note its
- # activity number -- this appears just afer the "¶" beginning
- # the activity record, for example "¶314"
- # 5 Save "••Recent Activities••" somewhere - NOTE the name must
- # start with two bullets, but is otherwise arbitrary, for example,
- # "••RA" or "••For undo"
- # 6 Add your saved version of "••Recent Activities••" to your
- # project, then close the window for "••Recent Activities••"
- # to save memory
- # 7 Select both the saved version of "••Recent Activities••" and
- # file A for MFS operations (hold down the <Option> key and
- # click on each name in the project window - a bullet • will
- # appear beside the file name). These two should be the only
- # files marked with a • in your project window
- # 8 Call up hAWK and select this program; the input option should
- # be "MFS selected files"; use "Set Variables" to set the value
- # of the variable "howFarBack" to the activity number that represents
- # how far back (inclusive) you wish to undo - for example,
- # howFarBack=314
- # -to undo from the present back to and including activity number 314.
- # 9 Click the Run button. The undone version of file A will
- # (eventually) be presented in the "$tempStdOut" window.
- # 10 If there are other files you wish to restore, add them to your project
- # if necessary and repeat steps 4-9 for each file.
- # NOTE while you're waiting you should avoid editing either
- # file A or your saved version of "••Recent Activities••".
-
- # Whew! Not trivial, but it does work.
-
- # One little bug: a spurious blank line may be appended to the undone
- # version of your file. This results from the way hAWK read lines (in
- # particular, expecting the last line of a file to end with a carriage return)
- # and the fix would be 1)difficult and 2)rather pointless. Let's live with it.
-
- ######### EnterAct's activity format #########
- # Generic activity format: '{}' means optional, 'OR' means exclusive or.
- # D stands for digit. Both "line" and "charPos" are D{D}.
- # "timestamp" is eg "Sun, Nov 22, 1992 1:03:07 AM"
- # ¶D{D}
- # timestamp activity «fname» line charPos {(temp) OR (obs) OR (undone)}
- # <=deleted D{D} characters{ (D{D} shown)}:
- # OR
- # =>inserted D{D} characters{ (D{D} shown)}:
- # «data»
- # ¬
- #
- # -where "charPos" is the character position of the start of the insert
- # or delete counting from the start of the line, and both "line" and
- # "charPos" are 1-based. Eg line 7 charPos 1 means the insert was just before
- # the first character on the seventh line.
- # -"shown" appears only when activity data is not fully shown.
-
- ######### Globals ##########
- # activityFile - full path name of activity file
- # undoFile - full path name of file being reverted
- # quotedUndoName - undo file name proper, inside «»
- # yActivity, yFName, yLine, yCharPos, yClass - set from the
- # "timestamp" line of the activity record
- # ySize, yShownSize - set from "<=deleted"/"=>inserted" line
- # numActivities - number of activities to be undone
- # ASize[] - size of data in bytes for activity
- # ADelete[] - 1 if delete, 0 if insert
- # ALine[] - starting line (1-based)
- # ACharPos[] - starting character position on line (1-based)
- # AText[] - contents of deletes only
- # AText2[] - overflow delete contents (up to 64K needed, but a hAWK string can
- # only hold up to 32K)
- # AText3[] - ditto
- # ANumCRs[] - number of carriage returns in edit text
- # ALLChars[] - number of characters in last line of edit
- # out[] - contents, line-by-line, of undoFile
-
- # User’s Manual references:
- # «hAWK User’s Manual» «F Running hAWK programs»
- # «hAWK User’s Manual» «L 5 Regular expressions»
- # «hAWK User’s Manual» «M 5 Built-in string and file functions»
- # «hAWK User’s Manual» «K 4 Built-in variables»
- # «hAWK User’s Manual» «K 8 Arrays»
- # «hAWK User’s Manual» «N User-defined functions»
- # «hAWK User’s Manual» «P 3 The getline function»
- # «hAWK User’s Manual» «O 3 Output into files»
- # «hAWK User’s Manual» «Q The hAWK function»
-
-
- ############ the program ###################
- BEGIN {
- permanent = 1
- temporary = 2
- obsolete = 3
- permUndone = 4
- WhichFileIsWhich()
- doingActivityFile = 1
- ##TEST ONLY
- ##DumpStats()
- LoadActivities()
- doingActivityFile = 0
- ##TEST ONLY
- ##DumpActivities()
- LoadUndoFileLines()
- UndoActivities()
- PrintUndoneVersionOfFile()
- }
-
- function WhichFileIsWhich( n,names)
- {
- if (ARGC > 3)
- Error("more than two files specified for input")
- n = split(ARGV[1], names, ":")
- if (match(names[n], /^••/))
- {
- activityFile = ARGV[1];
- undoFile = ARGV[2];
- }
- else
- {
- n = split(ARGV[2], names, ":")
- if (match(names[n], /^••/))
- {
- activityFile = ARGV[2];
- undoFile = ARGV[1];
- }
- else
- Error("\"...••Recent Activities••...\" file not specified")
- }
- n = split(undoFile, names, ":")
- quotedUndoName = "«" names[n] "»"
- }
-
- function LoadActivities()
- {
- SkipEarlyActivities()
- GetActivitiesToUndo()
- }
-
- function SkipEarlyActivities()
- {
-
- while ((getline < activityFile) > 0)
- {
- if (!match($0, /¶[0-9]+/))
- Error("activity number is unreadable");
- which = substr($0, RSTART+1, RLENGTH-1) + 0;
- if (which < howFarBack)
- {
- GetNextActivityLine() #"timestamp" etc line
- SkipRemainderOfActivity()
- }
- else if (which == howFarBack)
- break;
- else
- Error("activity number " howFarBack " was not found in sequence")
- }
- if (which == howFarBack)
- ; # bufferedLine = $0 -- but this line is not needed after all...
- else
- Error("activity number " howFarBack " was not found in sequence")
- }
-
- function SkipRemainderOfActivity()
- {
- # assumption: on "timestamp" etc line at this point
- GetNextActivityLine() #"<=deleted"/"=>inserted"/"¬"/other
- if (match($0, /^¬/))
- return;
- else if (match($0, /^<=deleted/) || match($0, /^=>inserted/))
- {
- GetActivitySizes()
- SkipActivityData()
- }
- GetNextActivityLine()
- if (match($0, /^¬/))
- return;
- else
- Error("unrecognized or missing activity line")
- }
-
- function GetNextActivityLine()
- {
- if ((getline < activityFile) <= 0)
- Error("unexpected early end of activity file")
- }
-
- function GetActivitySizes()
- {
- match($0, /[0-9]+/)
- ySize = substr($0, RSTART, RLENGTH)+0
- temp = substr($0, RSTART+RLENGTH)
- if (match(temp, /[0-9]+/))
- yShownSize = substr(temp, RSTART, RLENGTH)+0
- else
- yShownSize = ySize;
- }
-
- function SkipActivityData()
- {
- yShownSize += 2 # for the start and end quotes
- while (yShownSize > 0)
- {
- GetNextActivityLine()
- yShownSize -= length()
- if (yShownSize > 0) # not last line of data
- --yShownSize; # for CR at end of line
- }
- }
-
-
- function GetActivitiesToUndo()
- {
- while ((getline < activityFile) > 0) # "timestamp" line
- {
- ParseTimeLine()
- if (yFName == quotedUndoName && yClass == permanent)
- {
- GetNextActivityLine() #"<=deleted"/"=>inserted"/"¬"/other
- if (match($0, /^¬/))
- {
- SeeIfAnotherActivity()
- continue
- }
- else if (match($0, /^=>inserted/))
- {
- GetActivitySizes()
- if (ySize != yShownSize)
- Error("activity file is missing data for an insertion")
- GetNextActivityLine()
- RecordActivity(0)
- }
- else if (match($0, /^<=deleted/))
- {
- GetActivitySizes()
- if (ySize != yShownSize)
- Error("activity file is missing data for a deletion")
- GetNextActivityLine()
- RecordActivity(1)
- }
- GetNextActivityLine()
- if (!match($0, /^¬/))
- Error("end of activity not found")
- SeeIfAnotherActivity()
- }
- else # skip to next activity
- {
- SkipRemainderOfActivity()
- SeeIfAnotherActivity()
- }
- }
- }
-
- # timestamp activity «fname» line charPos {(temp) OR (obs)}
- # "timestamp" is eg "Sun, Nov 22, 1992 1:03:07 AM"
- # Sets yActivity, yFName, yLine, yCharPos, yClass
- function ParseTimeLine()
- {
- if (!match($0, /[APM]+/))
- Error("time stamp is missing from activity")
- $0 = substr($0, RSTART+RLENGTH+1) #skip timestamp and a space
- yActivity = $1
- if (!match($0, /«.*»/))
- {
- yFName = ""
- yLine = yCharPos = 0
- return
- }
- yFName = substr($0, RSTART, RLENGTH)
- $0 = substr($0, RSTART+RLENGTH+1)
- yLine = $1
- yCharPos = $2
- if ($3 == "")
- yClass = permanent
- else if ($3 == "(temp)")
- yClass = temporary
- else if ($3 == "(obs)")
- yClass = obsolete
- else if ($3 == "(undone)")
- yClass = permUndone
- else
- Error("unknown activity class")
- }
-
- function SeeIfAnotherActivity()
- {
- if ((getline < activityFile) <= 0)
- return;
- if (!match($0, /¶[0-9]+/))
- Error("expected but did not find ¶ followed by number");
- which = substr($0, RSTART+1, RLENGTH-1) + 0;
- }
-
- function RecordActivity(wasADelete, len, totalLen)
- {
- if (!match($0, /^«/))
- Error("data for an activity is missing")
- ASize[++numActivities] = ySize
- ADelete[numActivities] = wasADelete
- ALine[numActivities] = yLine
- ACharPos[numActivities] = yCharPos
-
- ## debug only
- AWhich[numActivities] = which
-
- yShownSize += 2 # for the start and end quotes
- ANumCRs[numActivities] = 0
- if (yShownSize <= length())
- {
- ALLChars[numActivities] = ySize
- if (wasADelete)
- AText[numActivities] = substr($0, 2, length() - 2)
- return
- }
- if (wasADelete)
- AText[numActivities] = substr($0, 2, length() - 1)
- yShownSize -= length()
- --yShownSize; # for CR at end of line
- if (wasADelete)
- {
- while (yShownSize > 0)
- {
- GetNextActivityLine()
- len = length()
- yShownSize -= len
- ANumCRs[numActivities]++
- if (yShownSize > 0) # not last line of data
- {
- --yShownSize; # for CR at end of line
- totalSize += len + 1
- if (totalSize > 64000)
- AText3[numActivities] = AText3[numActivities] "\n" $0
- else if (totalSize > 32000)
- AText2[numActivities] = AText2[numActivities] "\n" $0
- else
- AText[numActivities] = AText[numActivities] "\n" $0
- }
- else
- {
- --len
- totalSize += len + 1
- if (totalSize > 64000)
- AText3[numActivities] = AText3[numActivities] "\n" substr($0, 1, len)
- else if (totalSize > 32000)
- AText2[numActivities] = AText2[numActivities] "\n" substr($0, 1, len)
- else
- AText[numActivities] = AText[numActivities] "\n" substr($0, 1, len)
- ALLChars[numActivities] = len
- }
- }
- }
- else
- {
- while (yShownSize > 0)
- {
- GetNextActivityLine()
- len = length()
- yShownSize -= len
- ANumCRs[numActivities]++
- if (yShownSize > 0) # not last line of data
- --yShownSize; # for CR at end of line
- else
- ALLChars[numActivities] = len - 1
- }
- }
- }
-
- function LoadUndoFileLines()
- {
- while ((getline < undoFile) > 0)
- out[++outLines] = $0
- ++outLines # to compensate for the way hAWK reads lines - the last line of the
- # file may not have had a terminating carriage return.
- }
-
-
- function UndoActivities( i)
- {
- for (i = numActivities; i >= 1; --i)
- {
- if (ADelete[i])
- UndoDelete(i)
- else
- UndoInsert(i)
- }
- }
-
- #Put the deleted text back. All bookkeeping and arrays are 1-based.
- function UndoDelete(x, i, n, m, lineArr, lineArr2, lineArr3, trailer, startLine, numcrs)
- {
- startLine = ALine[x]
- if (ANumCRs[x] == 0)
- {
- out[startLine] = substr(out[startLine], 1, ACharPos[x] - 1)\
- AText[x] substr(out[startLine], ACharPos[x])
- }
- else
- {
- n = split(AText[x], lineArr, "\n")
- if (x in AText2)
- {
- m = split(AText2[x], lineArr2, "\n")
- for (i = 1; i <= m; ++i)
- {
- lineArr[n+i] = lineArr2[i]
- delete lineArr2[i]
- }
- if (x in AText3)
- {
- n += m
- m = split(AText3[x], lineArr3, "\n")
- for (i = 1; i <= m; ++i)
- {
- lineArr[n+i] = lineArr3[i]
- delete lineArr3[i]
- }
- }
- }
- n = ANumCRs[x] + 1
- trailer = substr(out[startLine], ACharPos[x])
- numcrs = ANumCRs[x]
- for (i = outLines; i > startLine; --i)
- out[i + numcrs] = out[i]
- outLines += numcrs
- out[startLine] = substr(out[startLine], 1, ACharPos[x] - 1) lineArr[1]
- for (i = 1; i < numcrs; ++i)
- out[startLine + i] = lineArr[i+1]
- out[startLine + numcrs] = lineArr[numcrs + 1] trailer
- }
- }
-
- #Take the inserted text out - note the inserted text as recorded in the
- # activity record is not needed.
- function UndoInsert(x, i, trailer, startLine, numcrs)
- {
- startLine = ALine[x]
- if (ANumCRs[x] == 0)
- {
- out[startLine] = substr(out[startLine], 1, ACharPos[x] - 1)\
- substr(out[startLine], ACharPos[x] + ASize[x]);
- }
- else
- {
- trailer = substr(out[startLine + ANumCRs[x]], ALLChars[x] + 1)
- out[startLine] = substr(out[startLine], 1, ACharPos[x] - 1) trailer
- numcrs = ANumCRs[x]
- for (i = startLine + numcrs + 1; i <= outLines; ++i)
- out[i - numcrs] = out[i]
- outLines -= numcrs
- }
- }
-
- # Reverted version is sent to stdout.
- function PrintUndoneVersionOfFile( i)
- {
- for (i = 1; i < outLines; ++i)
- print out[i]
- printf out[outLines]
- }
-
- function Error(msg)
- {
- print "Error, ", msg "."
- if (doingActivityFile)
- print FNR, $0
- exit
- }
-
- # For testing only from here to end.
- function DumpActivities( i)
- {
- print "activity file:", activityFile
- print "undo file:", undoFile
- print "quoted undo name:", quotedUndoName
- print "activity number to undo back to:", howFarBack
- print "number of activities to undo:", numActivities
- for (i = 1; i <= numActivities; ++i)
- {
- print "¶" AWhich[i], i
- print "activity size:", ASize[i], (ADelete[i] ? "deleted" : "inserted")
- print "Line:", ALine[i], "CharPos:", ACharPos[i]
- print "Num CRs:", ANumCRs[i], "Last line chars:", ALLChars[i]
- if (i in AText)
- print "«" AText[i] "»"
- print "¬"
- }
- exit
- }
-
- function GetActivitiesForStats()
- {
- while ((getline < activityFile) > 0) # "timestamp" line
- {
- ParseTimeLine()
- GetNextActivityLine() #"<=deleted"/"=>inserted"/"¬"/other
- if (match($0, /^¬/))
- {
- AWhich[++numActivities] = which
- SeeIfAnotherActivity()
- continue
- }
- else if (match($0, /^=>inserted/))
- {
- GetActivitySizes()
- if (ySize != yShownSize)
- Error("activity file is missing data for an insertion")
- GetNextActivityLine()
- RecordActivity(0)
- }
- else if (match($0, /^<=deleted/))
- {
- GetActivitySizes()
- if (ySize != yShownSize)
- Error("activity file is missing data for a deletion")
- GetNextActivityLine()
- RecordActivity(1)
- }
- else
- AWhich[++numActivities] = which;
- GetNextActivityLine()
- if (!match($0, /^¬/))
- Error("end of activity not found")
- SeeIfAnotherActivity()
- }
- }
-
- # Load all activities, print summary.
- function DumpStats()
- {
- howFarBack = 1
- SkipEarlyActivities()
- GetActivitiesForStats()
- PrintActivityStats() # exits
- }
-
- # A little stats function, used during development
- function PrintActivityStats( i, size, total, num)
- {
- for (i = 1; i <= numActivities; ++i)
- {
- size = ASize[i]
- total += size
- if (size+0 == 0)
- ++bin[0]
- else if (size < 500)
- ++bin[1]
- else if (size < 1000)
- ++bin[2]
- else if (size < 2000)
- ++bin[3]
- else if (size < 4000)
- ++bin[4]
- else if (size < 8000)
- ++bin[5]
- else if (size < 16000)
- ++bin[6]
- else if (size < 32000)
- ++bin[7]
- else if (size < 64000)
- ++bin[8]
- else
- ++bin[9]
- }
- print "number of activities:", numActivities
- print "total data bytes:", total
- print "average data per activity:", total/numActivities
- num = 500
- print "Activities by size:"
- print "-------------------"
- print "no data:", bin[o], (bin[0]/numActivities)*100, "%"
- for (i = 1; i <= 8; ++i)
- {
- print "under", num, ":", bin[i], (bin[i]/numActivities)*100, "%"
- num *= 2
- }
- print "over 64000:", bin[9], (bin[9]/numActivities)*100, "%"
- for (i = 1; i <= numActivities; ++i)
- print "¶" AWhich[i], i;
- exit
- }
-